package com.multimedia.room.recorder;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteOrder;

import com.multimedia.room.Recorder.OnStateChangedListener;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.util.Log;

public class Mp3Recorder implements OnCompletionListener, OnErrorListener {

	static final String SAMPLE_PREFIX = "recording";
	static final String SAMPLE_PATH_KEY = "sample_path";
	static final String SAMPLE_LENGTH_KEY = "sample_length";

	public static final int IDLE_STATE = 0;
	public static final int RECORDING_STATE = 1;
	public static final int PLAYING_STATE = 2;

	int mState = IDLE_STATE;

	public static final int NO_ERROR = 0;
	public static final int SDCARD_ACCESS_ERROR = 1;
	public static final int INTERNAL_ERROR = 2;
	public static final int IN_CALL_RECORD_ERROR = 3;
	private double mVolume;
	private int maxAmplitude = 0;

	public interface OnStateChangedListener {
		public void onStateChanged(int state);

		public void onError(int error);
	}

	OnStateChangedListener mOnStateChangedListener = null;

	long mSampleStart = 0; // time at which latest record or play operation
							// started
	int mSampleLength = 0; // length of current sample

	MediaPlayer mPlayer = null;

	private static final String TAG = Mp3Recorder.class.getSimpleName();

	private static final int DEFAULT_SAMPLING_RATE = 22050;

	private static final int FRAME_COUNT = 160;

	/* Encoded bit rate. MP3 file will be encoded with bit rate 32kbps */
	private static final int BIT_RATE = 32;

	private AudioRecord audioRecord = null;

	private int bufferSize;

	private File mSampleFile;

	private RingBuffer ringBuffer;

	private byte[] buffer;

	private FileOutputStream os = null;

	private DataEncodeThread encodeThread;

	private int samplingRate;

	private int channelConfig;

	private PCMFormat audioFormat;

	private boolean isRecording = false;
	Object mLock;

	public Mp3Recorder(int samplingRate, int channelConfig,
			PCMFormat audioFormat) {
		this.samplingRate = samplingRate;
		this.channelConfig = channelConfig;
		this.audioFormat = audioFormat;
		mLock = new Object();
	}

	/**
	 * Default constructor. Setup recorder with default sampling rate 1 channel,
	 * 16 bits pcm
	 */
	public Mp3Recorder() {
		this(DEFAULT_SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO,
				PCMFormat.PCM_16BIT);
	}

	/**
	 * Start recording. Create an encoding thread. Start record from this
	 * thread.
	 * 
	 * @throws IOException
	 */
	public void startRecording(File file) throws IOException {
		mSampleFile = file;
		if (isRecording)
			return;
		Log.d(TAG, "Start recording");
		Log.d(TAG, "BufferSize = " + bufferSize);
		// Initialize audioRecord if it's null.
		if (audioRecord == null) {
			initAudioRecorder();
		}
		initOutputFile();
		audioRecord.startRecording();

		new Thread() {

			@Override
			public void run() {
				isRecording = true;
				while (isRecording) {
					int bytes = audioRecord.read(buffer, 0, bufferSize);
					if (bytes > 0) {
						ringBuffer.write(buffer, bytes);
					}
					long v = 0;
					// 将 buffer 内容取出，进行平方和运算
					for (int i = 0; i < buffer.length; i++) {
						v += buffer[i] * buffer[i];
					}
					// 平方和除以数据总长度，得到音量大小。
					double mean = v / (double) bytes;
					double volume = 10 * Math.log10(mean);
					maxAmplitude = (int)mean*5;
					Log.d(TAG, "分贝值:" + volume + "  maxAmplitude = "+maxAmplitude);
				}
	                

				// release and finalize audioRecord
				try {
					audioRecord.stop();
					audioRecord.release();
					audioRecord = null;

					// stop the encoding thread and try to wait
					// until the thread finishes its job
					Message msg = Message.obtain(encodeThread.getHandler(),
							DataEncodeThread.PROCESS_STOP);
					msg.sendToTarget();

					Log.d(TAG, "waiting for encoding thread");
					encodeThread.join();
					Log.d(TAG, "done encoding thread");
				} catch (InterruptedException e) {
					Log.d(TAG, "Faile to join encode thread");
				} finally {
					if (os != null) {
						try {
							os.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}

			}
		}.start();
		mSampleStart = System.currentTimeMillis();
		setState(RECORDING_STATE);
	}

	/**
	 * 
	 * @throws IOException
	 */
	public void stopRecording() throws IOException {
		Log.d(TAG, "stop recording");
		isRecording = false;
		mSampleLength = (int) ((System.currentTimeMillis() - mSampleStart) / 1000);
		setState(IDLE_STATE);
	}

	/**
	 * Initialize audio recorder
	 */
	private void initAudioRecorder() throws IOException {
		int bytesPerFrame = audioFormat.getBytesPerFrame();
		/*
		 * Get number of samples. Calculate the buffer size (round up to the
		 * factor of given frame size)
		 */
		int frameSize = AudioRecord.getMinBufferSize(samplingRate,
				channelConfig, audioFormat.getAudioFormat()) / bytesPerFrame;
		if (frameSize % FRAME_COUNT != 0) {
			frameSize = frameSize + (FRAME_COUNT - frameSize % FRAME_COUNT);
			Log.d(TAG, "Frame size: " + frameSize);
		}

		bufferSize = frameSize * bytesPerFrame;

		/* Setup audio recorder */
		audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
				samplingRate, channelConfig, audioFormat.getAudioFormat(),
				bufferSize);

		// Setup RingBuffer. Currently is 10 times size of hardware buffer
		// Initialize buffer to hold data
		ringBuffer = new RingBuffer(10 * bufferSize);
		buffer = new byte[bufferSize];

		// Initialize lame buffer
		// mp3 sampling rate is the same as the recorded pcm sampling rate
		// The bit rate is 32kbps
		SimpleLame.init(samplingRate, 1, samplingRate, BIT_RATE);
		Log.d(TAG, "init audio recorder : " + frameSize);
	}

	private void initOutputFile() throws FileNotFoundException{
		os = new FileOutputStream(mSampleFile);
		// Create and run thread used to encode data
		// The thread will
		encodeThread = new DataEncodeThread(ringBuffer, os, bufferSize);
		encodeThread.start();
		audioRecord.setRecordPositionUpdateListener(encodeThread,
				encodeThread.getHandler());
		audioRecord.setPositionNotificationPeriod(FRAME_COUNT);
	}
	public void setOnStateChangedListener(OnStateChangedListener listener) {
		mOnStateChangedListener = listener;
	}

	public int state() {
		return mState;
	}

	public int progress() {
		if (mState == RECORDING_STATE || mState == PLAYING_STATE)
			return (int) ((System.currentTimeMillis() - mSampleStart) / 1000);
		return 0;
	}

	public int sampleLength() {
		return mSampleLength;
	}

	public File sampleFile() {
		return mSampleFile;
	}

	/**
	 * Resets the recorder state. If a sample was recorded, the file is deleted.
	 */
	public void delete() {
		stop();

		if (mSampleFile != null)
			mSampleFile.delete();

		mSampleFile = null;
		mSampleLength = 0;

		signalStateChanged(IDLE_STATE);
	}

	/**
	 * Resets the recorder state. If a sample was recorded, the file is left on
	 * disk and will be reused for a new recording.
	 */
	public void clear() {
		stop();

		mSampleLength = 0;

		signalStateChanged(IDLE_STATE);
	}

	public void startPlayback() {
		stop();

		mPlayer = new MediaPlayer();
		try {
			mPlayer.setDataSource(mSampleFile.getAbsolutePath());
			mPlayer.setOnCompletionListener(this);
			mPlayer.setOnErrorListener(this);
			mPlayer.prepare();
			mPlayer.start();
		} catch (IllegalArgumentException e) {
			setError(INTERNAL_ERROR);
			mPlayer = null;
			return;
		} catch (IOException e) {
			setError(SDCARD_ACCESS_ERROR);
			mPlayer = null;
			return;
		}

		mSampleStart = System.currentTimeMillis();
		setState(PLAYING_STATE);
	}

	public void stopPlayback() {
		if (mPlayer == null) // we were not in playback
			return;

		mPlayer.stop();
		mPlayer.release();
		mPlayer = null;
		setState(IDLE_STATE);
	}

	public void stop() {
		try {
			stopRecording();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		stopPlayback();
	}

	public boolean onError(MediaPlayer mp, int what, int extra) {
		stop();
		setError(SDCARD_ACCESS_ERROR);
		return true;
	}

	public void onCompletion(MediaPlayer mp) {
		stop();
	}

	private void setState(int state) {
		if (state == mState)
			return;

		mState = state;
		signalStateChanged(mState);
	}

	private void signalStateChanged(int state) {
		if (mOnStateChangedListener != null)
			mOnStateChangedListener.onStateChanged(state);
	}

	private void setError(int error) {
		if (mOnStateChangedListener != null)
			mOnStateChangedListener.onError(error);
	}

	public int getMaxAmplitude() {
		if (mState != RECORDING_STATE)
			return 0;

		return maxAmplitude;
	}
			
}